// ZipParser.cpp : Implementation of CZipParser

#include "stdafx.h"
#include "ZipParser.h"
#include "Inflate.h"


//This is the first entrypoint SharePoint is going to call 
STDMETHODIMP CZipParser::Parse( ILockBytes * pilb,  IParserPropertyBag * pibag,  VARIANT_BOOL * pfChanged)
{
	HRESULT returnValue = E_FAIL;
	*pfChanged = VARIANT_FALSE;
	bool callParserChain = true;

	//Get the Zip wrapper around the LockBytes object
	LUFILE* zippedBytes = lufopen(pilb);
	if(zippedBytes)
	{
		//Is it a zip file
		unzFile zippedXML = unzOpenInternal(zippedBytes);
		if(zippedXML)
		{
			MSXML2::IXMLDOMNodeListPtr parserAttempts = NULL;
			if(m_AlwaysReloadConfigXML)
			{
				MSXML2::IXMLDOMDocument3Ptr xmlConfiguration = GetXMLDOMDocument(m_ConfigurationXMLFilePath);
				parserAttempts = xmlConfiguration->selectNodes(L"//ZipParser/Parser");
			}
			else
			{
				//ok so now get a collection of parser nodes from the configuration file
				parserAttempts = m_XMLConfiguration->selectNodes(L"//ZipParser/Parser");
			}
			if(parserAttempts)
			{
				LONG listLength = parserAttempts->length;
				parserAttempts->reset();
				returnValue = S_OK;
				for(LONG listIndex = 0; listIndex < listLength; listIndex++)
				{
					//for this parser, try to find the requested xml file in the zip file 
					MSXML2::IXMLDOMNodePtr parserAttempt = parserAttempts->nextNode();
					MSXML2::IXMLDOMNodePtr fileNameNode =  parserAttempt->attributes->getNamedItem(L"XMLFileName");
					if(fileNameNode)
					{
						CStringW xmlFileName((BSTR) fileNameNode->text);
						if (UNZ_OK == unzLocateFile(zippedXML,xmlFileName,0))
						{
							//if we find any of our targets we wont chain to the alternate URL
							callParserChain = false;
							CHeapPtr<BYTE> memoryBuffer;
							//Just in case the zip estimate is off by a bit give it 100 bytes of breathing room
							//current max size is controlled in inflate.h and is set to 64K 
							size_t xmlStringLength = zippedXML->cur_file_info.uncompressed_size+100;
							if(!memoryBuffer.Allocate(xmlStringLength)) return E_OUTOFMEMORY;
							::ZeroMemory(memoryBuffer,xmlStringLength);
							//Open the file and unzip it into our memory buffer
							if (UNZ_OK == unzOpenCurrentFile(zippedXML))
							{
								int zipResult = unzReadCurrentFile(zippedXML,memoryBuffer.m_pData,zippedXML->cur_file_info.uncompressed_size);
								if(zipResult >=0)
								{
									//Turn it into a xml dom 
									MSXML2::IXMLDOMDocument3Ptr propertyDoc = GetXMLDOMDocument(memoryBuffer);
									if(propertyDoc)
									{
										ParseXMLIntoPropertyBag(propertyDoc,parserAttempt,pibag);
									}
								}
							}

						}
					}
				}
			}
			lufclose(zippedBytes);
		}
	}

	if(callParserChain || m_AlwaysCallOtherZipParser)
	{
		//If we did not find any of our target embeded XML files
		//and if there is another parser, then call it
		if(NULL != m_OtherZipParser)
		{
			returnValue = m_OtherZipParser->Parse(pilb,pibag,pfChanged);
			if(*pfChanged == VARIANT_TRUE)
			{
				//If our chained parser set this to true, SharePoint will 
				//cylce back and call Demote to push properties into the content
				//we need to "tell ourselves" to call the chain parser
				IParserProperty* lpChainFlag = NULL;
				HRESULT hRes = pibag->NewProp(PARSER_CHAIN_TRUE_PROPERTY_NAME,&lpChainFlag);
				if(SUCCEEDED(hRes))
				{
					lpChainFlag->SetBoolean(VARIANT_TRUE);
					lpChainFlag->Release();
				}

			}
		}

	}
	return returnValue;

}


STDMETHODIMP CZipParser::Demote( ILockBytes * pilb,  IParserPropertyBag * pibag,  VARIANT_BOOL * pfChanged)
{
	//This application does not support demotion, however if we chained to another parser and it
	//supports demotion, the we need to chain the demotion function as well. We stored off a "flag"
	//in the property bag if the chaining maked a change, which is what SharePoint uses to determine
	//if demotion should be called.
	*pfChanged = VARIANT_FALSE;
	if(NULL != m_OtherZipParser)
	{
		IParserProperty* lpChainFlag = NULL;
		HRESULT hRes = pibag->FindProp(PARSER_CHAIN_TRUE_PROPERTY_NAME,&lpChainFlag);
		if(SUCCEEDED(hRes))
		{
			VARIANT_BOOL chainFlagValue = VARIANT_FALSE;
			hRes = lpChainFlag->GetBoolean(&chainFlagValue);
			if(SUCCEEDED(hRes))
			{
				//We dont want to actually store our flag property in the content
				pibag->DeleteProp(PARSER_CHAIN_TRUE_PROPERTY_NAME);
				//Ok we told ourself to push the chain value
				if(VARIANT_TRUE == chainFlagValue)
				{
					HRESULT returnValue =  m_OtherZipParser->Demote(pilb,pibag,pfChanged);
					return returnValue;
				}
			}
		}
	}
	return S_OK;

}

STDMETHODIMP CZipParser::ExtractThumbnail( ILockBytes * pilb,  IStream * pistmThumbnail)
{
	//Ok so its a bit self serving but I put my company logo in as a resource
	//and thats what the thumbnail will be, if we wanted to be "nice we could
	//open the zip and look for the first bmp or jpg file and return it
	HRESULT returnValue = E_FAIL;
	HRSRC bitMapResource= bitMapResource = ::FindResource(_AtlBaseModule.m_hInst,MAKEINTRESOURCE(IDR_V3LOGOJPEG),L"BINARY");
	if(bitMapResource)
	{
		HGLOBAL bitMapData = ::LoadResource(_AtlBaseModule.m_hInst, bitMapResource);
		if(bitMapData)
		{
			//Apparently we cant open a stream on the actual bit map
			//so first copy the data to a block we can attach to
			DWORD bitMapSize = ::SizeofResource(_AtlBaseModule.m_hInst, bitMapResource);
			HGLOBAL bitMapData = ::LoadResource(_AtlBaseModule.m_hInst, bitMapResource);
			if(bitMapData)
			{
				returnValue = S_FALSE;
				HGLOBAL glbMem = ::GlobalAlloc(GHND , (SIZE_T) bitMapSize);
				if(!glbMem) returnValue = E_OUTOFMEMORY;
				if(SUCCEEDED(returnValue))
				{
					LPBYTE lpDestination = (LPBYTE)::GlobalLock(glbMem);
					LPBYTE lpSource = (LPBYTE)::LockResource(bitMapData);

					::CopyMemory(lpDestination,lpSource,bitMapSize);
					::GlobalUnlock(glbMem);
					::GlobalUnlock(bitMapData);

					IStream* bitMapStream = NULL;
					HRESULT hRes = ::CreateStreamOnHGlobal(glbMem,TRUE,&bitMapStream);
					if(SUCCEEDED(hRes))
					{
						//Now copy our stream to the one SharePoint sent us
						ULARGE_INTEGER sizeOfImage ;
						ULARGE_INTEGER pcbRead;
						ULARGE_INTEGER pcbWritten;
						sizeOfImage.QuadPart = (ULONGLONG) bitMapSize;
						hRes = pistmThumbnail->SetSize(sizeOfImage);
						if(SUCCEEDED(hRes))hRes = bitMapStream->CopyTo(pistmThumbnail,sizeOfImage,&pcbRead,&pcbWritten);
						if(SUCCEEDED(hRes))
						{
							if(pcbRead.QuadPart == sizeOfImage.QuadPart && pcbWritten.QuadPart == sizeOfImage.QuadPart)
							{
								returnValue = S_OK;
							}
						}
						else
						{
							returnValue = hRes;
						}
						bitMapStream->Release();
					}
				}
			}
		}
	}
	return returnValue;
}


